package org.activequant.math.simulation;

import java.util.Iterator;
import java.util.Random;
/**
 * HMM2Generator implements Iterator&lt;Double&gt;.<br/>
 * Holds the following associated variables:
 * <ul>
 * <li>random(Random)</li>
 * <li>expert1(Iterator&lt;Double&gt;)</li>
 * <li>expert2(Iterator&lt;Double&gt;)</li>
 * <li>a11(double)</li>
 * <li>a22(double)</li>
 * <li>state(int)</li>
 * </ul>
 * @author Dimitar
 *
 */
public class HMM2Generator implements Iterator<Double> {
	/**
	 * private final Random random = new Random();
	 */
	private final Random random = new Random();
	/**
	 * private Iterator&lt;Double&gt; expert1 = new GaussianRandomGenerator();
	 */
	private Iterator<Double> expert1 = new GaussianRandomGenerator();
	/**
	 * private Iterator&lt;Double&gt; expert2 = new GaussianRandomGenerator();
	 */
	private Iterator<Double> expert2 = new GaussianRandomGenerator();
	/**
	 * private double a11 = 1.0;
	 */
	private double a11 = 1.0;
	/**
	 * private double a22 = 1.0;
	 */
	private double a22 = 1.0;
	/**
	 * sets the associated a11(double) with the given val(double)
	 * @param val
	 */
	public void setA11(double val) {
		a11 = val;
	}
	/**
	 * returns the associated a11(double)
	 * @return
	 */
	public double getA11() {
		return a11;
	}
	/**
	 * sets the associated a22(double) with the given val(double)
	 * @param val
	 */
	public void setA22(double val) {
		a22 = val;
	}
	/**
	 * returns the associated a22(double)
	 * @return
	 */
	public double getA22() {
		return a22;
	}
	/**
	 * sets the associated expert1(Iterator&lt;Double&gt;) with the given val(Iterator&lt;Double&gt;)
	 * @param val
	 */
	public void setExpert1(Iterator<Double> val) {
		expert1 = val;
	}
	/**
	 * sets the associated expert2(Iterator&lt;Double&gt;) with the given val(Iterator&lt;Double&gt;)
	 * @param val
	 */
	public void setExpert2(Iterator<Double> val) {
		expert2 = val;
	}
	/**
	 * private int state = 1;
	 */
	private int state = 1;
	/**
	 * sets the associated state(int) with the given val(int)
	 * @param val
	 */
	public void setState(int val) {
		state = val;
	}
	/**
	 * returns the associated state(int)
	 * @return
	 */
	public int getState() {
		return state;
	}
	/**
	 * returns true
	 */
	public boolean hasNext() {
		return true;
	}
	/**
	 * this updates the associated state(int) state to 1 or 2,
	 * and returns the next random variable using the associated expert1(Iterator&lt;Double&gt;) or expert2(Iterator&lt;Double&gt;)
	 */
	public Double next() {
		// do we stay or do we switch?
		double r = random.nextDouble();
		switch(state) {
		case 1:
			if(r >= a11) state = 2;
			break;
		case 2:
			if(r >= a22) state = 1;
			break;
		default:
			throw new IllegalStateException("invalid internal state: " + state);
		}
		
		// let appropriate expert emit the value
		return (state == 1) ? expert1.next() : expert2.next();
	}
	/**
	 * throws an UnsupportedOperationException
	 */
	public void remove() {
		throw new UnsupportedOperationException();
	}
}
